package com.alks.function.service.impl.finance.salary;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.DateUtils;
import com.alks.common.utils.IdCardUtil;
import com.alks.common.utils.UserInfoUtil;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.common.utils.excelUtils.ExcelPoiUtils;
import com.alks.common.utils.excelUtils.ExcelUtils;
import com.alks.common.utils.stringUtils.ZStringUtils;
import com.alks.entity.data.entity.*;
import com.alks.entity.data.enums.CompanyEnum;
import com.alks.entity.data.enums.chengkong.DefineEnum;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.administration.PcEmpAttendListDto;
import com.alks.function.data.dto.finance.salary.*;
import com.alks.function.data.request.finance.salary.PcSalaryNewListRequest;
import com.alks.function.data.request.finance.salary.UpdateRemarkRequest;
import com.alks.function.mapper.administration.PcEmpAttendMapper;
import com.alks.function.mapper.administration.PcEmployeeMapper;
import com.alks.function.mapper.finance.salary.*;
import com.alks.function.service.finance.salary.PersonnelService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PersonnelServiceImpl extends ServiceImpl<PersonnelMapper, PcSalaryNew> implements PersonnelService {

    @Resource
    private PersonnelMapper personnelMapper;

    @Autowired
    private PcEmpAttendMapper pcEmpAttendMapper;

    @Autowired
    private PcEmployeeMapper pcEmployeeMapper;

    @Autowired
    private PayrollClosureMapper payrollClosureMapper;

    @Autowired
    private SubsidyDeductionMapper subsidyDeductionMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private PcSalaryRateMapper pcSalaryRateMapper;

    @Autowired
    private PcSalaryDeptMapper pcSalaryDeptMapper;


    /**
     * 取数
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo fetchEmployee(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years,userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm,years).eq(PcSalaryNew::getCompanyId,userInfo.getCompanyId());
        personnelMapper.delete(wrapper);
        List<EmployeeListDto> employeeList = personnelMapper.getEmployeeList(years, userInfo.getCompanyId());
        List<PcSalaryNew> pcSalaryNews = BeanUtil.copyList(employeeList, PcSalaryNew.class);
        for (PcSalaryNew pcSalaryNew : pcSalaryNews) {
            pcSalaryNew.setCompanyId(userInfo.getCompanyId());
            pcSalaryNew.setSysDate(LocalDateTime.now());
            pcSalaryNew.setSysUser(userInfo.getUserName());
            pcSalaryNew.setUpdateDate(LocalDateTime.now());
            pcSalaryNew.setUpdateUser(userInfo.getUserName());
            pcSalaryNew.setYyyymm(years);
            pcSalaryNew.setBasicSalary(null);
            pcSalaryNew.setPDeptName("全部");
        }
        personnelMapper.addPcSalaryNewList(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(),userInfo.getUserName(),years+"工资月份--取数");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 资料明细
     * @param request
     * @return
     */
    @Override
    @AutoPageAop
    public ResponseInfo PcSalaryNewList(PcSalaryNewListRequest request) {
        Map<String, Object> map = new HashMap();
        String companyId = UserIdThread.get().getCompanyId();
        request.setCompanyId(companyId);
        List<PcSalaryNewDto> pcSalaryNewList = personnelMapper.getPcSalaryNewList(request);
        PcSalaryNewCountDto count = personnelMapper.getPcSalaryNewCount(request);
        if (ZStringUtils.isNotEmpty(pcSalaryNewList)) {
            for (PcSalaryNewDto t : pcSalaryNewList) {
                int age = IdCardUtil.calculateAgeFromIdCard(t.getIdNo());
                if (age!=-1){
                    t.setAge(age);
                }
                zeroToNull(t);
                if (!ObjectUtils.isEmpty(t.getFlag())){
                    t.setFlag(DefineEnum.CHECKOUT_FLAG.getValue());
                }
            }
        }
        PageInfo pageInfo = new PageInfo<>(pcSalaryNewList);
        map.put("pcSalaryNewList", pcSalaryNewList);
        map.put("count", count);
        map.put("total", pageInfo.getTotal());
        return ResponseInfo.ok(map);
    }

    /**
     * 设置0变成null
     * @param dto
     */
    private void zeroToNull(PcSalaryNewDto dto){
        if (dto.getAttendance() != null && dto.getAttendance().compareTo(BigDecimal.ZERO) == 0) {
            dto.setAttendance(null);
        }
        if (dto.getAttendanceOver() != null && dto.getAttendanceOver().compareTo(BigDecimal.ZERO) == 0) {
            dto.setAttendanceOver(null);
        }
        if (dto.getAttendanceAct() != null && dto.getAttendanceAct().compareTo(BigDecimal.ZERO) == 0) {
            dto.setAttendanceAct(null);
        }
        if (dto.getBasicSalary() != null && dto.getBasicSalary().compareTo(BigDecimal.ZERO) == 0) {
            dto.setBasicSalary(null);
        }
        if (dto.getPositionAlw() != null && dto.getPositionAlw().compareTo(BigDecimal.ZERO) == 0) {
            dto.setPositionAlw(null);
        }
        if (dto.getPositionAlw2() != null && dto.getPositionAlw2().compareTo(BigDecimal.ZERO) == 0) {
            dto.setPositionAlw2(null);
        }
        if (dto.getSalaryAlw() != null && dto.getSalaryAlw().compareTo(BigDecimal.ZERO) == 0) {
            dto.setSalaryAlw(null);
        }
        if (dto.getMinSalary() != null && dto.getMinSalary().compareTo(BigDecimal.ZERO) == 0) {
            dto.setMinSalary(null);
        }
        if (dto.getOverSalary() != null && dto.getOverSalary().compareTo(BigDecimal.ZERO) == 0) {
            dto.setOverSalary(null);
        }
        if (dto.getAllowance() != null && dto.getAllowance().compareTo(BigDecimal.ZERO) == 0) {
            dto.setAllowance(null);
        }
        if (dto.getInjuryAlw() != null && dto.getInjuryAlw().compareTo(BigDecimal.ZERO) == 0) {
            dto.setInjuryAlw(null);
        }
        if (dto.getIntroduceBonus() != null && dto.getIntroduceBonus().compareTo(BigDecimal.ZERO) == 0) {
            dto.setIntroduceBonus(null);
        }
        if (dto.getLeaveAlw() != null && dto.getLeaveAlw().compareTo(BigDecimal.ZERO) == 0) {
            dto.setLeaveAlw(null);
        }
        if (dto.getUnionM() != null && dto.getUnionM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setUnionM(null);
        }
        if (dto.getTaxM() != null && dto.getTaxM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setTaxM(null);
        }
        if (dto.getClothesM() != null && dto.getClothesM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setClothesM(null);
        }
        if (dto.getWaterEleM() != null && dto.getWaterEleM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setWaterEleM(null);
        }
        if (dto.getMealM() != null && dto.getMealM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setMealM(null);
        }
        if (dto.getAbsenceM() != null && dto.getAbsenceM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setAbsenceM(null);
        }
        if (dto.getWaterM() != null && dto.getWaterM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setWaterM(null);
        }
        if (dto.getRepayM() != null && dto.getRepayM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setRepayM(null);
        }
        if (dto.getOtherM() != null && dto.getOtherM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setOtherM(null);
        }
        if (dto.getMinusM() != null && dto.getMinusM().compareTo(BigDecimal.ZERO) == 0) {
            dto.setMinusM(null);
        }
        if(dto.getCarFee() != null && dto.getCarFee().compareTo(BigDecimal.ZERO) == 0){
            dto.setCarFee(null);
        }
    }

    /**
     * 取月考勤资料
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo RetrieveAttendanceData(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years,userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm,years).eq(PcSalaryNew::getCompanyId,userInfo.getCompanyId());
        List<PcSalaryNew> pcSalaryNews = personnelMapper.selectList(wrapper);
        if (pcSalaryNews.size() == 0){
            return ResponseInfo.error("请先取数!");
        }
        List<String> psHoliday2 = pcEmpAttendMapper.getPsHoliday2(years);
        int attendanceOver=0;
        if (ZStringUtils.isNotEmpty(psHoliday2)){
            attendanceOver=psHoliday2.size();
        }
        List<PcEmpAttendListDto> pcEmpAttendList = pcEmpAttendMapper.getPcEmpAttendList(userInfo.getCompanyId(), years);
        if (ZStringUtils.isEmpty(pcEmpAttendList)){
            return ResponseInfo.error("考勤记录为空!");
        }
        int daysInMonth = DateUtils.getDaysInMonth(years);
        List<PcEmployeeWorkTypeChg> typeChgList = pcEmployeeMapper.getEmployeeWorkTypeChgList(years, userInfo.getCompanyId());
        Map<String, List<PcEmployeeWorkTypeChg>> chgListMap=new HashMap<>();
        if (!ObjectUtils.isEmpty(typeChgList)){
             chgListMap = typeChgList.stream()
                    .sorted(Comparator.comparing(PcEmployeeWorkTypeChg::getSysDate))
                    .collect(Collectors.groupingBy(PcEmployeeWorkTypeChg::getIdNo));
        }
        Map<String, List<PcEmpAttendListDto>> dateList = getPcEmpAttendList(years, userInfo.getCompanyId());
        List<PcSalaryNewDetail> pcSalaryNewDetails = new ArrayList<>();
        for (PcSalaryNew t : pcSalaryNews) {
            t.setAttendanceOver(attendanceOver);
            t.setAttendance(daysInMonth);
            t.setUpdateUser(userInfo.getUserName());
            t.setUpdateDate(LocalDateTime.now());
            if (ObjectUtils.isNotEmpty(pcEmpAttendList)) {
                for (PcEmpAttendListDto pcEmpAttend : pcEmpAttendList) {
                    if (t.getWorkerId().equals(pcEmpAttend.getWorkerId())) {
                        BigDecimal attendanceAct = pcEmpAttend.getWorkHours().divide(BigDecimal.valueOf(10));
                        t.setAttendanceAct(attendanceAct);
                        t.setDelDays(pcEmpAttend.getLeaveDays());
                        t.setOverTime(pcEmpAttend.getOutDays());
                        t.setNsTime(pcEmpAttend.getLateNums());
                        t.setLateTimes(pcEmpAttend.getLateMinutes());
                        break;
                    }
                }
            }else {
                t.setAttendanceAct(BigDecimal.ZERO);
                t.setDelDays(BigDecimal.ZERO);
                t.setOverTime(BigDecimal.ZERO);
                t.setNsTime(BigDecimal.ZERO);
                t.setLateTimes(BigDecimal.ZERO);
            }
            if (!ObjectUtils.isEmpty(chgListMap)){
                List<PcEmployeeWorkTypeChg> chgList = chgListMap.get(t.getIdNo());
                if (ZStringUtils.isNotEmpty(chgList)){
                    getPcSalaryNewDetailList(years, pcSalaryNewDetails, t.getWorkerId(), chgList);
                }
            }
        }
        if (ZStringUtils.isNotEmpty(pcSalaryNewDetails)) {
            for (PcSalaryNewDetail newDetail : pcSalaryNewDetails) {
                BigDecimal workHours = getWorkHours(dateList, newDetail.getWorkerId(), newDetail.getBeginDate(), newDetail.getEndDate());
                BigDecimal workDays = workHours.divide(BigDecimal.valueOf(10));
                BigDecimal salary = (newDetail.getBasicSalary() != null && workDays.compareTo(BigDecimal.ZERO) != 0) ?
                        newDetail.getBasicSalary().multiply(workDays).divide(BigDecimal.valueOf(daysInMonth),2,RoundingMode.HALF_UP):
                        BigDecimal.ZERO;
                newDetail.setWorkDays(workDays);
                newDetail.setSalary(salary);
            }
            personnelMapper.delPcSalaryNewDetail(years,userInfo.getCompanyId());
            personnelMapper.addPcSalaryNewDetails(pcSalaryNewDetails);
        }
        personnelMapper.updateByEmpAttend(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(),userInfo.getUserName(),years+"工资月份--取月考勤资料");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 获取工种变更记录时间
     * @param years
     * @param pcSalaryNewDetails
     * @param workerId
     * @param chgList
     */
    private void getPcSalaryNewDetailList(String years, List<PcSalaryNewDetail> pcSalaryNewDetails, String workerId, List<PcEmployeeWorkTypeChg> chgList) {
        LocalDate firstDayInMonth = DateUtils.getFirstDayInMonth(years);
        LocalDate lastDayInMonth = DateUtils.getLastDayInMonth(years);
        PcEmployeeWorkTypeChg previousChg = null;
        for (PcEmployeeWorkTypeChg chg : chgList) {
            PcSalaryNewDetail newDetail = new PcSalaryNewDetail();
            BeanUtil.copyProperties(chg, newDetail);
            if (previousChg == null) {
                newDetail.setBeginDate(chg.getDateIn().toLocalDate().isBefore(firstDayInMonth) ? firstDayInMonth : LocalDate.from(chg.getDateIn()));
            } else {
                newDetail.setBeginDate(LocalDate.from(previousChg.getSysDate()));
                newDetail.setCalcType(previousChg.getNewCalcType());
                newDetail.setWorkType(previousChg.getNewWorkType());
            }
            newDetail.setWorkerId(workerId);
            newDetail.setYyyymm(years);
            newDetail.setEndDate(LocalDate.from(chg.getSysDate().minusDays(1)));
            newDetail.setBasicSalary(chg.getOldBasicSalary());

            pcSalaryNewDetails.add(newDetail);
            previousChg = chg;
        }
        PcSalaryNewDetail lastDetail = new PcSalaryNewDetail();
        BeanUtil.copyProperties(chgList.get(chgList.size() - 1), lastDetail);
        lastDetail.setWorkerId(workerId);
        lastDetail.setYyyymm(years);
        lastDetail.setBeginDate(LocalDate.from(chgList.get(chgList.size() - 1).getSysDate()));
        lastDetail.setEndDate(lastDayInMonth);
        lastDetail.setWorkType(chgList.get(chgList.size() - 1).getNewWorkType());
        lastDetail.setCalcType(chgList.get(chgList.size() - 1).getNewCalcType());
        if (chgList.size()-1==0){
            lastDetail.setBasicSalary(chgList.get(chgList.size() - 1).getBasicSalary());
        }else {
            lastDetail.setBasicSalary(chgList.get(chgList.size() - 1).getBasicSalary());
        }
        pcSalaryNewDetails.add(lastDetail);
    }

    /**
     * 查询本月份所有考勤
     * @param years
     * @param companyId
     */
    private  Map<String, List<PcEmpAttendListDto>> getPcEmpAttendList(String years,String companyId) {
        List<PcEmpAttendListDto> list = pcEmpAttendMapper.getPcEmpAttendDateList(companyId, years);
        if (!ObjectUtils.isEmpty(list)){
            Map<String, List<PcEmpAttendListDto>> listMap = list.stream().sorted(Comparator.comparing(PcEmpAttendListDto::getDdate))
                    .collect(Collectors.groupingBy(PcEmpAttendListDto::getWorkerId));
            return listMap;
        }else {
            return null;
        }
    }

    /**
     * 计算变岗天数以及工资
     * @param listMap
     * @param workerId
     * @param beginDate
     * @param endDate
     * @return
     */
    private BigDecimal getWorkHours(Map<String, List<PcEmpAttendListDto>> listMap, String workerId, LocalDate beginDate, LocalDate endDate) {
        if (!ObjectUtils.isEmpty(listMap)) {
            List<PcEmpAttendListDto> dtoList = listMap.get(workerId);
            if (ZStringUtils.isNotEmpty(dtoList)) {
                BigDecimal workHours = dtoList.stream()
                        .filter(dto -> {
                            LocalDate date = dto.getDdate();
                            return !date.isBefore(beginDate) && !date.isAfter(endDate);
                        })
                        .map(PcEmpAttendListDto::getWorkHours)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                return workHours;
            }
        }
        return BigDecimal.ZERO;
    }

    /**
     * 检查入参
     * @param years
     * @param companyId
     */
    private void CheckParameters(String years, String companyId) {
        if (ObjectUtils.isEmpty(years)) {
            throw new ServiceErrorException("请输入结算月份!");
        }
        boolean yearMonth = DateUtils.isValidYearMonth(years);
        if (!yearMonth) {
            throw new ServiceErrorException("请输入正确的结算月份!");
        }
        String salaryFlag = personnelMapper.getSalaryFlag(years,companyId);
        if ("Y".equals(salaryFlag)){
            throw new ServiceErrorException(years + "月份工资已检核,不能处理!");
        }
    }

    /**
     * 1 检核月考勤资料/2 取消检核月考勤资料
     * @param map
     * @param choose
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo CheckMonthlyAttendance(Map<String, String> map, Integer choose) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years,userInfo.getCompanyId());
        List<PcEmpAttendListDto> pcEmpAttendList = pcEmpAttendMapper.getPcEmpAttendList(userInfo.getCompanyId(), years);
        if (ZStringUtils.isEmpty(pcEmpAttendList)){
            return ResponseInfo.error("考勤记录为空!");
        }
        String flag = pcEmpAttendMapper.getPcEmpAttendFlag(userInfo.getCompanyId(), years);
        switch (choose){
            case 1:
                if ("Y".equals(flag)){
                    return ResponseInfo.error("该月份考勤已检核,不能检核!");
                }
                pcEmpAttendMapper.updateByFlag(userInfo.getCompanyId(),userInfo.getUserName(),LocalDateTime.now(),"Y",years);
                addPcSalaryLog(userInfo.getCompanyId(),userInfo.getUserName(),years+"工资月份--检核月考勤资料");
                break;
            case 2:
                if (!"Y".equals(flag)){
                    return ResponseInfo.error("该月份考勤未检核,不能取消检核!");
                }
                pcEmpAttendMapper.updateByFlag(userInfo.getCompanyId(),null,null,null,years);
                addPcSalaryLog(userInfo.getCompanyId(),userInfo.getUserName(),years+"工资月份--取消检核月考勤资料");
                break;
            default:
                return ResponseInfo.error("请检查参数!");
        }
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 1 检核月工资/2 取消检核月工资
     * @param map
     * @param choose
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo CheckMonthlySalary(Map<String, String> map, Integer choose) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        if (ObjectUtils.isEmpty(years)) {
            return ResponseInfo.error("请输入结算月份!");
        }
        boolean yearMonth = DateUtils.isValidYearMonth(years);
        if (!yearMonth) {
            return ResponseInfo.error("请输入正确的结算月份!");
        }
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId());
        Integer count = personnelMapper.selectCount(wrapper);
        if (ObjectUtils.isEmpty(count) && count == 0) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        Integer pcSalaryAct = personnelMapper.getPcSalaryAct(userInfo.getCompanyId(), years);
        if (ObjectUtils.isEmpty(pcSalaryAct) || pcSalaryAct == 0) {
            return ResponseInfo.error("该月份存在实发工资为空的记录!");
        }
        String flag = pcEmpAttendMapper.getPcEmpAttendFlag(userInfo.getCompanyId(), years);
        if (!"Y".equals(flag)) {
            return ResponseInfo.error("该月份考勤未检核,不能处理!");
        }
        String salaryFlag = personnelMapper.getSalaryFlag(years, userInfo.getCompanyId());
        switch (choose) {
            case 1:
                if ("Y".equals(salaryFlag)) {
                    return ResponseInfo.error(years + "月份工资已检核,不能处理!");
                }
                personnelMapper.updateBySalaryFlag(userInfo.getCompanyId(), userInfo.getUserName(), LocalDateTime.now(), "Y", years);
                String k = CompanyEnum.updateId(UserIdThread.get().getCompanyId())+"KK";
                String balanceNo = redisService.slipNoGenerate(k, 4, 1);
                subsidyDeductionMapper.updatePcCostDetail2ToBalanceNo(userInfo.getCompanyId(), years, balanceNo);
                addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--检核月工资");
                break;
            case 2:
                String bonusYm = payrollClosureMapper.getBonusYm(years, userInfo.getCompanyId());
                if (!ObjectUtils.isEmpty(bonusYm)) {
                    return ResponseInfo.error("该月份工资已封账,不能取消检核!");
                }
                if (!"Y".equals(salaryFlag)) {
                    return ResponseInfo.error(years + "月份工资未检核,不能处理!");
                }
                personnelMapper.updateBySalaryFlag(userInfo.getCompanyId(), null, null, null, years);
                subsidyDeductionMapper.updatePcCostDetail2ToBalanceNo(userInfo.getCompanyId(), years, null);
                addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--取消检核月工资");
                break;
            default:
                return ResponseInfo.error("请检查参数!");
        }
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 插入财务操作日志
     */
    private void  addPcSalaryLog(String companyId,String userName,String remark){
        PcSalaryLog entity = new PcSalaryLog();
        entity.setCompanyId(companyId);
        entity.setSysUser(userName);
        entity.setSysDate(LocalDateTime.now());
        entity.setRemark(remark);
        personnelMapper.addPcSalaryLog(entity);
    }

    /**
     * 清空资料
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo delPcSalaryNewList(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId());
        Integer count = personnelMapper.selectCount(wrapper);
        if (ObjectUtils.isEmpty(count) || count == 0) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        String bonusYm = payrollClosureMapper.getBonusYm(years, userInfo.getCompanyId());
        if (!ObjectUtils.isEmpty(bonusYm)) {
            return ResponseInfo.error("该月份工资已封账,不能清空资料!");
        }
        LambdaQueryWrapper<PcSalaryNew> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId());
        personnelMapper.delete(wrapper2);
        personnelMapper.delPcSalaryNewDetail(years, userInfo.getCompanyId());
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--清空资料");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 调岗明细列表
     * @param yyyymm
     * @param workerId
     * @return
     */
    @Override
    public ResponseInfo getPcSalaryNewDetailList(String yyyymm, String workerId) {
        List<PcSalaryNewDetail> list = personnelMapper.getPcSalaryNewDetailList(yyyymm, workerId, UserIdThread.get().getCompanyId());
        return ResponseInfo.ok(list);
    }

    /**
     * 取计时工资
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo TakeHourlyWages(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        List<PcSalaryNewDto> employeeList = personnelMapper.getEmployeeToSalaryList(years, userInfo.getCompanyId());
        if (ObjectUtils.isEmpty(employeeList)) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        List<PcSalaryNewDetail> details = personnelMapper.getPcSalaryNewDetails(years, userInfo.getCompanyId());
        Map<String, List<PcSalaryNewDetail>> listMap = new HashMap<>();
        if (ZStringUtils.isNotEmpty(details)) {
            listMap = details.stream().collect(Collectors.groupingBy(PcSalaryNewDetail::getWorkerId));
        }
        List<PcSalaryNew> pcSalaryNews = BeanUtil.copyList(employeeList, PcSalaryNew.class);
        for (PcSalaryNew t : pcSalaryNews) {
                BigDecimal basicSalary = t.getBasicSalary() != null ? t.getBasicSalary() : BigDecimal.ZERO;
                BigDecimal attendanceAct = t.getAttendanceAct() != null ? t.getAttendanceAct() : BigDecimal.ZERO;
                BigDecimal attendance = t.getAttendance() != null ? BigDecimal.valueOf(t.getAttendance()) : BigDecimal.ZERO;
                BigDecimal positionAlw = basicSalary.multiply(attendanceAct).divide(attendance, 2, RoundingMode.HALF_UP);
                t.setPositionAlw(positionAlw);
                if (ZStringUtils.isNotEmpty(listMap)) {
                    List<PcSalaryNewDetail> newDetails = listMap.get(t.getWorkerId());
                    if (ZStringUtils.isNotEmpty(newDetails)) {
                        BigDecimal salary = newDetails.stream().map(PcSalaryNewDetail::getSalary).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                        t.setPositionAlw(salary);
                    }
            }
            BigDecimal leaveAlw = getLeaveAlw(t);
            t.setLeaveAlw(leaveAlw);
            t.setUpdateUser(userInfo.getUserName());
            t.setUpdateDate(LocalDateTime.now());
        }
        personnelMapper.updateByPositionAlw(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--取计时工资");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     *  计算补贴小计
     * @param t
     */
    public BigDecimal getLeaveAlw(PcSalaryNew t) {
        List<BigDecimal> values = Arrays.asList(
                t.getPositionAlw(), t.getPositionAlw2(), t.getSalaryAlw(),
                t.getMinSalary(), t.getOverSalary(), t.getAllowance(),
                t.getInjuryAlw(), t.getIntroduceBonus()
        );
        return values.stream()
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 计算扣款小计
     * @param t
     * @return
     */
    public BigDecimal getMinusM(PcSalaryNew t){
        List<BigDecimal> values = Arrays.asList(
                t.getUnionM(), t.getTaxM(), t.getClothesM(),
                t.getWaterEleM(), t.getMealM(), t.getAbsenceM(),
                t.getWaterM(), t.getRepayM(), t.getOtherM(),t.getCarFee());
        return values.stream()
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 取计件工资
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo PieceRateSalary(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryNew> pcSalaryNews = personnelMapper.selectList(wrapper);
        if (ObjectUtils.isEmpty(pcSalaryNews)) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        List<CutSalaryList> cutSalaryList = personnelMapper.getCutSalaryList(years, userInfo.getCompanyId());
        List<CutSalaryList> stitchSalaryList = personnelMapper.getStitchSalaryList(years, userInfo.getCompanyId());
        for (PcSalaryNew t : pcSalaryNews) {
            BigDecimal positionAlw2=BigDecimal.ZERO;
            if (ZStringUtils.isNotEmpty(cutSalaryList)){
                for (CutSalaryList salaryList : cutSalaryList) {
                    if (t.getWorkerId().equals(salaryList.getWorkerId())){
                        BigDecimal amt = salaryList.getAmt() != null ? salaryList.getAmt() : BigDecimal.ZERO;
                        positionAlw2=positionAlw2.add(amt);
                    }
                }
            }
            if (ZStringUtils.isNotEmpty(stitchSalaryList)){
                for (CutSalaryList stitchSalary : stitchSalaryList) {
                    if (t.getWorkerId().equals(stitchSalary.getWorkerId())){
                        BigDecimal amt = stitchSalary.getAmt() != null ? stitchSalary.getAmt() : BigDecimal.ZERO;
                        positionAlw2=positionAlw2.add(amt);
                    }
                }
            }
            t.setPositionAlw2(positionAlw2);
            BigDecimal leaveAlw = getLeaveAlw(t);
            t.setLeaveAlw(leaveAlw);
            t.setUpdateUser(userInfo.getUserName());
            t.setUpdateDate(LocalDateTime.now());
        }
        personnelMapper.updateByPositionAlw2(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--取计件工资");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 取绩效工资
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo getMeritPay(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId()).in(PcSalaryNew::getWorkType, "针车课长", "针车组长");
        List<PcSalaryNew> pcSalaryNews = personnelMapper.selectList(wrapper);
        if (ObjectUtils.isEmpty(pcSalaryNews)) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        List<CutSalaryList> stitchDeptSalaryList = personnelMapper.getStitchDeptSalaryList(years, userInfo.getCompanyId());
        List<CutSalaryList> stitchDeptSalaryList2 = personnelMapper.getStitchDeptSalaryList2(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryRate> eq = new LambdaQueryWrapper<>();
        eq.eq(PcSalaryRate::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryRate> salaryRates = pcSalaryRateMapper.selectList(eq);
        LambdaQueryWrapper<PcSalaryDept> eq2 = new LambdaQueryWrapper<>();
        eq2.eq(PcSalaryDept::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryDept> pcSalaryDepts = pcSalaryDeptMapper.selectList(eq2);
        Map<String, List<PcSalaryDept>> deptMap = pcSalaryDepts.stream()
                .collect(Collectors.groupingBy(PcSalaryDept::getDeptName));
        for (PcSalaryNew pcSalaryNew : pcSalaryNews) {
            if ("针车组长".equals(pcSalaryNew.getWorkType())) {
                BigDecimal salaryAlw = getSalaryAlw(stitchDeptSalaryList, salaryRates, pcSalaryNew.getDeptName());
                pcSalaryNew.setSalaryAlw(salaryAlw);
            }
            if ("针车课长".equals(pcSalaryNew.getWorkType())) {
                BigDecimal salaryAlw = getSalaryAlw2(stitchDeptSalaryList2, deptMap, pcSalaryNew.getDeptName());
                pcSalaryNew.setSalaryAlw(salaryAlw);
            }
            BigDecimal leaveAlw = getLeaveAlw(pcSalaryNew);
            pcSalaryNew.setLeaveAlw(leaveAlw);
            pcSalaryNew.setUpdateUser(userInfo.getUserName());
            pcSalaryNew.setUpdateDate(LocalDateTime.now());
        }
        personnelMapper.updateBySalaryAlw(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--取绩效工资");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 计算针车课长绩效
     * @param list
     * @param deptMap
     * @param deptName
     * @return
     */
    private BigDecimal getSalaryAlw2(List<CutSalaryList> list, Map<String, List<PcSalaryDept>> deptMap, String deptName) {
        BigDecimal salaryAlw = BigDecimal.ZERO;
        if (list == null || list.isEmpty() || deptMap == null || deptMap.isEmpty()) {
            return salaryAlw;
        }
        List<PcSalaryDept> depts = deptMap.getOrDefault(deptName, Collections.emptyList());
        for (PcSalaryDept dept : depts) {
            BigDecimal rate = dept.getRate() != null ? dept.getRate() : BigDecimal.ZERO;
            BigDecimal totalAmt = list.stream()
                    .filter(t -> dept.getDeptName2().equals(t.getDeptName()))
                    .map(t -> t.getAmt() != null ? t.getAmt() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            salaryAlw = salaryAlw.add(totalAmt.multiply(rate));
        }
        return salaryAlw;
    }


    /**
     * 计算针车组长绩效
     * @param list
     * @param salaryRates
     * @param deptName
     * @return
     */
    public BigDecimal getSalaryAlw(List<CutSalaryList> list, List<PcSalaryRate> salaryRates, String deptName) {
        BigDecimal salaryAlw = BigDecimal.ZERO;
        if (list == null || list.isEmpty()) {
            return salaryAlw;
        }
        for (CutSalaryList t : list) {
            BigDecimal amt = t.getAmt() != null ? t.getAmt() : BigDecimal.ZERO;
            if (deptName.equals(t.getDeptName())) {
                for (PcSalaryRate salaryRate : salaryRates) {
                    BigDecimal amt1 = salaryRate.getAmt1() != null ? salaryRate.getAmt1() : BigDecimal.ZERO;
                    BigDecimal amt2 = salaryRate.getAmt2() != null ? salaryRate.getAmt2() : BigDecimal.ZERO;
                    if (amt1.compareTo(amt) < 0 && amt2.compareTo(amt) > 0) {
                        BigDecimal rate = salaryRate.getRate();
                        BigDecimal calculatedSalaryAlw = amt.multiply(rate).setScale(2, RoundingMode.HALF_UP);
                        return calculatedSalaryAlw;
                    }
                }
            }
        }
        return salaryAlw;
    }

    /**
     * 计算工资
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo CalculateSalary(Map<String, String> map) {
        String years = map.get("years");
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryNew> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PcSalaryNew::getYyyymm, years).eq(PcSalaryNew::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryNew> pcSalaryNews = personnelMapper.selectList(wrapper);
        if (ObjectUtils.isEmpty(pcSalaryNews)) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        List<PcCostDetail2Dto> list = subsidyDeductionMapper.getPcCostDetail2ListByFinanceYymm(years, userInfo.getCompanyId());
        Map<String, List<PcCostDetail2Dto>> listMap = list.stream()
                .collect(Collectors.groupingBy(PcCostDetail2Dto::getVendorNo));
        for (PcSalaryNew t : pcSalaryNews) {
            List<PcCostDetail2Dto> dtoList = listMap.getOrDefault(t.getWorkerId(), Collections.emptyList());
            getSubsidyDeduction(t,dtoList);
            BigDecimal leaveAlw = getLeaveAlw(t);
            t.setLeaveAlw(leaveAlw);
            BigDecimal minusM = getMinusM(t);
            t.setMinusM(minusM);
            t.setSalaryAct(leaveAlw.subtract(minusM).setScale(0, RoundingMode.HALF_UP));
            t.setUpdateUser(userInfo.getUserName());
            t.setUpdateDate(LocalDateTime.now());
        }
        personnelMapper.updateBySalaryAct(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years + "工资月份--计算工资");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 计算员工补贴扣款
     * @param t
     * @param list
     */
    public void getSubsidyDeduction(PcSalaryNew t, List<PcCostDetail2Dto> list){
        BigDecimal minSalary = list.stream()
                .filter(dto -> "补贴".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        t.setMinSalary(minSalary);
        BigDecimal overSalary = list.stream()
                .filter(dto -> "加班工资".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        t.setOverSalary(overSalary);
        BigDecimal allowance = list.stream()
                .filter(dto -> "奖励".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        t.setAllowance(allowance);
        BigDecimal injuryAlw = list.stream()
                .filter(dto -> "补发".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        t.setInjuryAlw(injuryAlw);
        BigDecimal introduceBonus = list.stream()
                .filter(dto -> "其他".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        t.setIntroduceBonus(introduceBonus);
        BigDecimal unionM = list.stream()
                .filter(dto -> "社医保".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setUnionM(unionM);
        BigDecimal taxM = list.stream()
                .filter(dto -> "个税".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setTaxM(taxM);
        BigDecimal clothesM = list.stream()
                .filter(dto -> "厂服/厂牌".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setClothesM(clothesM);
        BigDecimal waterEleM = list.stream()
                .filter(dto -> "水电费".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setWaterEleM(waterEleM);
        BigDecimal mealM = list.stream()
                .filter(dto -> "迟到漏打".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setMealM(mealM);
        BigDecimal absenceM = list.stream()
                .filter(dto -> "旷工乐捐".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setAbsenceM(absenceM);
        BigDecimal waterM = list.stream()
                .filter(dto -> "倒欠工资".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setWaterM(waterM);
        BigDecimal repayM = list.stream()
                .filter(dto -> "个人借款".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setRepayM(repayM);
        BigDecimal otherM = list.stream()
                .filter(dto -> "其他乐捐".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setOtherM(otherM);
        BigDecimal carFee = list.stream()
                .filter(dto -> "停车费".equals(dto.getTypeName()))
                .map(dto -> dto.getAmt() != null ? dto.getAmt() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add).negate();
        t.setCarFee(carFee);
    }

    /**
     * 修改备注
     * @param request
     * @return
     */
    @Override
    public ResponseInfo UpdateRemark(UpdateRemarkRequest request) {
        if (ObjectUtils.isEmpty(request.getWorkerId())){
            return ResponseInfo.error("工号不能为空!");
        }
        if (ObjectUtils.isEmpty(request.getIdNo())){
            return ResponseInfo.error("身份证不能为空!");
        }
        if (ObjectUtils.isEmpty(request.getCompanyId())){
            return ResponseInfo.error("公司代码不能为空!");
        }
        if (ObjectUtils.isEmpty(request.getYyyymm())){
            return ResponseInfo.error("月份不能为空!");
        }
        LambdaUpdateWrapper<PcSalaryNew> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(PcSalaryNew::getWorkerId,request.getWorkerId())
                .eq(PcSalaryNew::getYyyymm,request.getYyyymm())
                .eq(PcSalaryNew::getIdNo,request.getIdNo())
                .eq(PcSalaryNew::getCompanyId,request.getCompanyId())
                .set(PcSalaryNew::getRemark,request.getRemark())
                .set(PcSalaryNew::getUpdateDate,LocalDateTime.now())
                .set(PcSalaryNew::getUpdateUser,UserIdThread.get().getUserName());
        personnelMapper.update(null,wrapper);
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 重算工资
     * @param request
     * @return
     */
    @Override
    @Transactional
    public ResponseInfo RecalculateSalary(List<UpdateRemarkRequest> request) {
        if (ObjectUtils.isEmpty(request)) {
            return ResponseInfo.error("请选择记录!");
        }
        Map<String, List<UpdateRemarkRequest>> rMap = request.stream().collect(Collectors.groupingBy(UpdateRemarkRequest::getYyyymm));
        String years = rMap.keySet().stream().findFirst().get();
        if (rMap.size() > 1) {
            return ResponseInfo.error("不能处理多个月份的数据!");
        }
        UserInfoUtil.UserInfo userInfo = UserInfoUtil.getUserInfo();
        CheckParameters(years, userInfo.getCompanyId());
        List<PcSalaryNew> pcSalaryNews = personnelMapper.getPcSalaryNewList2(request);
        if (ObjectUtils.isEmpty(pcSalaryNews)) {
            return ResponseInfo.error("该月份没有工资明细记录,不能处理!");
        }
        List<String> psHoliday2 = pcEmpAttendMapper.getPsHoliday2(years);
        int attendanceOver = 0;
        if (ZStringUtils.isNotEmpty(psHoliday2)) {
            attendanceOver = psHoliday2.size();
        }
        List<PcEmpAttendListDto> pcEmpAttendList2 = pcEmpAttendMapper.getPcEmpAttendList2(request);
        int daysInMonth = DateUtils.getDaysInMonth(years);
        List<PcEmployeeWorkTypeChg> typeChgList2 = pcEmployeeMapper.getEmployeeWorkTypeChgList2(request);
        Map<String, List<PcEmployeeWorkTypeChg>> chgListMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(typeChgList2)) {
            chgListMap = typeChgList2.stream().sorted(Comparator.comparing(PcEmployeeWorkTypeChg::getSysDate))
                    .collect(Collectors.groupingBy(PcEmployeeWorkTypeChg::getIdNo));
        }
        Map<String, List<PcEmpAttendListDto>> dateList = getPcEmpAttendList2(request);
        List<PcSalaryNewDetail> pcSalaryNewDetails = new ArrayList<>();
        //取考勤资料
        for (PcSalaryNew t : pcSalaryNews) {
            t.setAttendanceOver(attendanceOver);
            t.setAttendance(daysInMonth);
            if (ObjectUtils.isNotEmpty(pcEmpAttendList2)) {
                for (PcEmpAttendListDto pcEmpAttend : pcEmpAttendList2) {
                    if (t.getWorkerId().equals(pcEmpAttend.getWorkerId())) {
                        BigDecimal attendanceAct = pcEmpAttend.getWorkHours().divide(BigDecimal.valueOf(10));
                        t.setAttendanceAct(attendanceAct);
                        t.setDelDays(pcEmpAttend.getLeaveDays());
                        t.setOverTime(pcEmpAttend.getOutDays());
                        t.setNsTime(pcEmpAttend.getLateNums());
                        t.setLateTimes(pcEmpAttend.getLateMinutes());
                        break;
                    }
                }
            }else {
                t.setAttendanceAct(BigDecimal.ZERO);
                t.setDelDays(BigDecimal.ZERO);
                t.setOverTime(BigDecimal.ZERO);
                t.setNsTime(BigDecimal.ZERO);
                t.setLateTimes(BigDecimal.ZERO);
            }
            if (!ObjectUtils.isEmpty(chgListMap)) {
                List<PcEmployeeWorkTypeChg> chgList = chgListMap.get(t.getIdNo());
                if (ZStringUtils.isNotEmpty(chgList)) {
                    getPcSalaryNewDetailList(years, pcSalaryNewDetails, t.getWorkerId(), chgList);
                }
            }
        }

        //写入岗位变更明细
        if (ZStringUtils.isNotEmpty(pcSalaryNewDetails)) {
            for (PcSalaryNewDetail newDetail : pcSalaryNewDetails) {
                BigDecimal workHours = getWorkHours(dateList, newDetail.getWorkerId(), newDetail.getBeginDate(), newDetail.getEndDate());
                BigDecimal workDays = workHours.divide(BigDecimal.valueOf(10));
                BigDecimal salary = (newDetail.getBasicSalary() != null && workDays.compareTo(BigDecimal.ZERO) != 0) ?
                        newDetail.getBasicSalary().multiply(workDays).divide(BigDecimal.valueOf(daysInMonth), 2, RoundingMode.HALF_UP) :
                        BigDecimal.ZERO;
                newDetail.setWorkDays(workDays);
                newDetail.setSalary(salary);
            }
        }
        //计时计件
        List<CutSalaryList> cutSalaryList2 = personnelMapper.getCutSalaryList2(request);
        List<CutSalaryList> stitchSalaryList2 = personnelMapper.getStitchSalaryList2(request);
        //绩效
        List<CutSalaryList> stitchDeptSalaryList3 = personnelMapper.getStitchDeptSalaryList3(request);
        List<CutSalaryList> stitchDeptSalaryList4 = personnelMapper.getStitchDeptSalaryList2(years, userInfo.getCompanyId());
        LambdaQueryWrapper<PcSalaryRate> eq = new LambdaQueryWrapper<>();
        eq.eq(PcSalaryRate::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryRate> salaryRates = pcSalaryRateMapper.selectList(eq);
        LambdaQueryWrapper<PcSalaryDept> eq2 = new LambdaQueryWrapper<>();
        eq2.eq(PcSalaryDept::getCompanyId, userInfo.getCompanyId());
        List<PcSalaryDept> pcSalaryDepts = pcSalaryDeptMapper.selectList(eq2);
        Map<String, List<PcSalaryDept>> deptMap = pcSalaryDepts.stream()
                .collect(Collectors.groupingBy(PcSalaryDept::getDeptName));

        //补贴扣款
        List<PcCostDetail2Dto> list = subsidyDeductionMapper.getPcCostDetail2ListByWorkerId(request);
        Map<String, List<PcCostDetail2Dto>> SubsidyListMap = list.stream()
                .collect(Collectors.groupingBy(PcCostDetail2Dto::getVendorNo));

        //取计时工资
        Map<String, List<PcSalaryNewDetail>> listMap = new HashMap<>();
        listMap = pcSalaryNewDetails.stream().collect(Collectors.groupingBy(PcSalaryNewDetail::getWorkerId));
        for (PcSalaryNew t : pcSalaryNews) {
            BigDecimal basicSalary = t.getBasicSalary() != null ? t.getBasicSalary() : BigDecimal.ZERO;
            BigDecimal attendanceAct = t.getAttendanceAct() != null ? t.getAttendanceAct() : BigDecimal.ZERO;
            BigDecimal attendance = t.getAttendance() != null ? BigDecimal.valueOf(t.getAttendance()) : BigDecimal.ZERO;
            BigDecimal positionAlw = basicSalary.multiply(attendanceAct).divide(attendance, 2, RoundingMode.HALF_UP);
            t.setPositionAlw(positionAlw);
            if (ZStringUtils.isNotEmpty(listMap)) {
                List<PcSalaryNewDetail> newDetails = listMap.get(t.getWorkerId());
                if (ZStringUtils.isNotEmpty(newDetails)) {
                    BigDecimal salary = newDetails.stream().map(PcSalaryNewDetail::getSalary).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                    t.setPositionAlw(salary);
                }
            }
            //计件工资
            BigDecimal positionAlw2 = BigDecimal.ZERO;
            if (ZStringUtils.isNotEmpty(cutSalaryList2)) {
                if (ZStringUtils.isNotEmpty(cutSalaryList2)) {
                    for (CutSalaryList salaryList : cutSalaryList2) {
                        if (t.getWorkerId().equals(salaryList.getWorkerId())) {
                            BigDecimal amt = salaryList.getAmt() != null ? salaryList.getAmt() : BigDecimal.ZERO;
                            positionAlw2 = positionAlw2.add(amt);
                        }
                    }
                }
            }
            if (ZStringUtils.isNotEmpty(stitchSalaryList2)) {
                for (CutSalaryList stitchSalary : stitchSalaryList2) {
                    if (t.getWorkerId().equals(stitchSalary.getWorkerId())) {
                        BigDecimal amt = stitchSalary.getAmt() != null ? stitchSalary.getAmt() : BigDecimal.ZERO;
                        positionAlw2 = positionAlw2.add(amt);
                    }
                }
            }

            //绩效工资
            t.setPositionAlw2(positionAlw2);
            if ("针车组长".equals(t.getWorkType())) {
                BigDecimal salaryAlw = getSalaryAlw(stitchDeptSalaryList3, salaryRates, t.getDeptName());
                t.setSalaryAlw(salaryAlw);
            }
            if ("针车课长".equals(t.getWorkType())) {
                BigDecimal salaryAlw = getSalaryAlw2(stitchDeptSalaryList4, deptMap, t.getDeptName());
                t.setSalaryAlw(salaryAlw);
            }

            //补贴扣款
            List<PcCostDetail2Dto> dtoList = SubsidyListMap.getOrDefault(t.getWorkerId(), Collections.emptyList());
            getSubsidyDeduction(t, dtoList);
            BigDecimal leaveAlw = getLeaveAlw(t);
            t.setLeaveAlw(leaveAlw);
            BigDecimal minusM = getMinusM(t);
            t.setMinusM(minusM);
            t.setSalaryAct(leaveAlw.subtract(minusM).setScale(0, RoundingMode.HALF_UP));
            t.setUpdateUser(userInfo.getUserName());
            t.setUpdateDate(LocalDateTime.now());
        }
        if (ZStringUtils.isNotEmpty(pcSalaryNewDetails)) {
            personnelMapper.delPcSalaryNewDetail2(request);
            personnelMapper.addPcSalaryNewDetails(pcSalaryNewDetails);
        }
        personnelMapper.updatePcSalaryNews(pcSalaryNews);
        addPcSalaryLog(userInfo.getCompanyId(), userInfo.getUserName(), years+"工资月份--重算工资");
        return ResponseInfo.ok("操作成功!");
    }

    /**
     * 根据条件查询考勤记录
     * @param request
     * @return
     */
    private  Map<String, List<PcEmpAttendListDto>> getPcEmpAttendList2(List<UpdateRemarkRequest> request) {
        List<PcEmpAttendListDto> list = pcEmpAttendMapper.getPcEmpAttendDateList2(request);
        if (!ObjectUtils.isEmpty(list)){
            Map<String, List<PcEmpAttendListDto>> listMap = list.stream().sorted(Comparator.comparing(PcEmpAttendListDto::getDdate))
                    .collect(Collectors.groupingBy(PcEmpAttendListDto::getWorkerId));
            return listMap;
        }else {
            return null;
        }
    }

    /**
     * 导出工资核算表
     * @param request
     * @param response
     */
    @Override
    public void ExportSalaryAccountingTable(PcSalaryNewListRequest request, HttpServletResponse response) {
        request.setCompanyId(UserIdThread.get().getCompanyId());
        List<PcSalaryNewDto> newList = personnelMapper.getPcSalaryNewList(request);
        if (ZStringUtils.isEmpty(newList)){
            throw new ServiceErrorException("该月份没有工资核算明细信息!");
        }
        for (PcSalaryNewDto t : newList) {
            zeroToNull(t);
        }
        String fileName ="工资核算表"+"_" +DateUtils.dateTimeNow() + ".xls";
        PcSalaryNewCountDto newCount = personnelMapper.getPcSalaryNewCount(request);
        Map<String, Object> mapList = new HashMap<>();
        mapList.put("years",request.getYyyymm());
        mapList.put("date",LocalDate.now());
        mapList.put("mapList",newList);
        mapList.put("t1",newCount);
        try {
            TemplateExportParams templateParams = new TemplateExportParams("templates/工资核算表模板.xls", true);
            ExcelUtils.setResponseZF(response, fileName);
            Workbook workbook = ExcelExportUtil.exportExcel(templateParams, mapList);
            workbook.write(response.getOutputStream());
            workbook.close();
        } catch (Exception e) {
            log.error("工资核算表导出失败, 错误信息: {}", e.getMessage());
            throw new ServiceErrorException("工资核算表导出失败!");
        }
    }

    /**
     * 导出工资签核表
     * @param request
     * @param response
     */
    @Override
    public void ExportSalaryApprovalForm(PcSalaryNewListRequest request, HttpServletResponse response) {
        request.setCompanyId(UserIdThread.get().getCompanyId());
        //查询职工
        request.setPosition("职工");
        List<PcSalaryNewDto> newList = personnelMapper.getPcSalaryNewList(request);
        if (ZStringUtils.isEmpty(newList)){
            throw new ServiceErrorException("该月份没有工资签核明细信息!");
        }
        String fileName ="薪资签字表"+"_" +DateUtils.dateTimeNow() + ".xls";
        Map<String, List<PcSalaryNewDto>> listMap = newList.stream().collect(Collectors.groupingBy(PcSalaryNewDto::getDeptName)).entrySet().stream()
                .sorted((t1, t2) -> t1.getKey().compareToIgnoreCase(t2.getKey()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue,
                        LinkedHashMap::new
                ));
        Map<Integer, List<Map<String, Object>>> mapList = new HashMap<>();
        int sheetIndex = 0;
        for (Map.Entry<String, List<PcSalaryNewDto>> entry : listMap.entrySet()) {
            String deptName = entry.getKey();
            List<PcSalaryNewDto> salaryList = entry.getValue();
            List<Map<String, Object>> exportList = new ArrayList<>();
            for (int i = 0; i < salaryList.size(); i++) {
                int j = i + 1;
                PcSalaryNewDto dto = salaryList.get(i);
                dto.setCfmFlag(String.valueOf(j));
                if (dto.getLeaveAlw() != null) {
                    getMinSalary(dto);
                    getSubsidy(dto);
                    BigDecimal salary = getSalary(dto);
                    dto.setPositionAlw(salary);
                    BigDecimal deduction = getDeduction(dto);
                    BigDecimal salaryAct = dto.getLeaveAlw().subtract(deduction).setScale(0, RoundingMode.HALF_UP);
                    dto.setSalaryAct(salaryAct);
                }
                zeroToNull(dto);
            }
            Map<String, Object> sheetData = new HashMap<>();
            sheetData.put("sheetName", deptName);
            sheetData.put("deptName", deptName);
            sheetData.put("date", LocalDate.now());
            sheetData.put("mapList", salaryList);
            exportList.add(sheetData);
            PcSalaryNewCountDto countDto = new PcSalaryNewCountDto();
            getSalaryCount(countDto, salaryList);
            sheetData.put("t1", countDto);
            mapList.put(sheetIndex, exportList);
            sheetIndex++;

        }
        try {
            TemplateExportParams templateParams = new TemplateExportParams("templates/薪资签字表模板.xls", true);
            ExcelUtils.setResponseZF(response, fileName);
            Workbook workbook = ExcelExportUtil.exportExcelClone(mapList,templateParams);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sheet = workbook.getSheetAt(i);
                sheet.setRepeatingRows(new CellRangeAddress(0, 2, 0, 20));
                sheet.setColumnHidden(3, true);
                sheet.setColumnHidden(5, true);
                sheet.setColumnHidden(6, true);
            }
            workbook.write(response.getOutputStream());
            workbook.close();
        } catch (Exception e) {
            log.error("薪资签字表导出失败, 错误信息: {}", e.getMessage());
            throw new ServiceErrorException("薪资签字表导出失败!");
        }
    }

    /**
     * 计算补贴
     * @param dto
     */
    public void getMinSalary(PcSalaryNewDto dto) {
        BigDecimal leaveAlw = dto.getLeaveAlw();
        if (leaveAlw.compareTo(BigDecimal.valueOf(600)) <= 0) {
            dto.setMinSalary(BigDecimal.ZERO);
        } else if (leaveAlw.compareTo(BigDecimal.valueOf(4000)) <= 0) {
            dto.setMinSalary(BigDecimal.valueOf(500));
        } else if (leaveAlw.compareTo(BigDecimal.valueOf(6000)) <= 0) {
            dto.setMinSalary(BigDecimal.valueOf(600));
        } else if (leaveAlw.compareTo(BigDecimal.valueOf(8000)) <= 0) {
            dto.setMinSalary(BigDecimal.valueOf(700));
        } else {
            dto.setMinSalary(BigDecimal.valueOf(800));
        }
    }

    /**
     * 计算加班工资/绩效工资/计件工资
     * @param dto
     */
    public void getSubsidy(PcSalaryNewDto dto) {
        BigDecimal leaveAlw = dto.getLeaveAlw();
        BigDecimal allowance = dto.getAllowance() != null ? dto.getAllowance() : BigDecimal.ZERO;
        BigDecimal minSalary = dto.getMinSalary()!= null ? dto.getMinSalary() : BigDecimal.ZERO;
        BigDecimal baseSalary = leaveAlw.subtract(allowance).subtract(minSalary);
        BigDecimal overSalary=BigDecimal.ZERO;
        BigDecimal salaryAlw = BigDecimal.ZERO;
        BigDecimal positionAlw2 = BigDecimal.ZERO;
        if (leaveAlw.compareTo(BigDecimal.valueOf(4000)) <= 0) {
            overSalary =getFormula(0.45, baseSalary);
            salaryAlw = getFormula(0.05, baseSalary);
            positionAlw2 = getFormula(0.5, baseSalary);
        } else if (leaveAlw.compareTo(BigDecimal.valueOf(6000)) <= 0) {
            overSalary =getFormula(0.5, baseSalary);
            salaryAlw = getFormula(0.1, baseSalary);
            positionAlw2 = getFormula(0.4, baseSalary);
        } else if (leaveAlw.compareTo(BigDecimal.valueOf(8000)) <= 0){
            overSalary =getFormula(0.5, baseSalary);
            salaryAlw = getFormula(0.15, baseSalary);
            positionAlw2 = getFormula(0.35, baseSalary);
        }else {
            overSalary =getFormula(0.5, baseSalary);
            salaryAlw = getFormula(0.2, baseSalary);
            positionAlw2 = getFormula(0.3, baseSalary);
        }
        dto.setOverSalary(overSalary);
        dto.setSalaryAlw(salaryAlw);
        dto.setPositionAlw2(positionAlw2);
    }

    /**
     * 计算公式
     * @param value
     * @param salary
     * @return
     */
    public BigDecimal getFormula(Double value,BigDecimal salary){
        return salary.multiply(BigDecimal.valueOf(value)).setScale(0,RoundingMode.UP);
    }

    /**
     * 计算应发工资
     * @param t
     * @return
     */
    public BigDecimal getSalary(PcSalaryNewDto t) {
        List<BigDecimal> values = Arrays.asList(
                t.getPositionAlw2(), t.getSalaryAlw(),
                t.getMinSalary(), t.getOverSalary(), t.getAllowance()
        );
        return values.stream()
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 计算扣款工资
     * @param t
     * @return
     */
    public BigDecimal getDeduction(PcSalaryNewDto t){
        List<BigDecimal> values = Arrays.asList(
                t.getUnionM(), t.getTaxM(), t.getClothesM(),
                t.getWaterEleM(), t.getMealM(), t.getAbsenceM(),
                t.getWaterM(), t.getRepayM(), t.getOtherM(),t.getCarFee());
        return values.stream()
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 计算各工资列表统计合计
     * @param dto
     */
    public void getSalaryCount(PcSalaryNewCountDto dto, List<PcSalaryNewDto> list) {
        BigDecimal leaveAlw = list.stream().map(PcSalaryNewDto::getLeaveAlw).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setLeaveAlw(leaveAlw);
        BigDecimal positionAlw2 = list.stream().map(PcSalaryNewDto::getPositionAlw2).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setPositionAlw2(positionAlw2);
        BigDecimal salaryAlw = list.stream().map(PcSalaryNewDto::getSalaryAlw).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setSalaryAlw(salaryAlw);
        BigDecimal overSalary = list.stream().map(PcSalaryNewDto::getOverSalary).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setOverSalary(overSalary);
        BigDecimal allowance = list.stream().map(PcSalaryNewDto::getAllowance).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setAllowance(allowance);
        BigDecimal minSalary = list.stream().map(PcSalaryNewDto::getMinSalary).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setMinSalary(minSalary);
        BigDecimal positionAlw = list.stream().map(PcSalaryNewDto::getPositionAlw).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setPositionAlw(positionAlw);
        BigDecimal repayM = list.stream().map(PcSalaryNewDto::getRepayM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setRepayM(repayM);
        BigDecimal waterEleM = list.stream().map(PcSalaryNewDto::getWaterEleM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setWaterEleM(waterEleM);
        BigDecimal clothesM = list.stream().map(PcSalaryNewDto::getClothesM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setClothesM(clothesM);
        BigDecimal waterM = list.stream().map(PcSalaryNewDto::getWaterM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setWaterM(waterM);
        BigDecimal otherM = list.stream().map(PcSalaryNewDto::getOtherM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setOtherM(otherM);
        BigDecimal salaryAct = list.stream().map(PcSalaryNewDto::getSalaryAct).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setSalaryAct(salaryAct);
        BigDecimal unionM= list.stream().map(PcSalaryNewDto::getUnionM).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setUnionM(unionM);
        BigDecimal carFee= list.stream().map(PcSalaryNewDto::getCarFee).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setCarFee(carFee);
    }

    /**
     * 导出银行确认表
     * @param request
     * @param response
     */
    @Override
    public void ExportSalaryBank(PcSalaryNewListRequest request, HttpServletResponse response) {
        request.setCompanyId(UserIdThread.get().getCompanyId());
        //查询职工
        request.setPosition("职工");
        List<PcSalaryNewDto> newList = personnelMapper.getPcSalaryNewList(request);
        if (ZStringUtils.isEmpty(newList)){
            throw new ServiceErrorException("该月份没有银行确认表信息!");
        }

        //汇总数据
        for (int i = 0; i < newList.size(); i++) {
            int j = i + 1;
            PcSalaryNewDto dto = newList.get(i);
            dto.setCfmFlag(String.valueOf(j));
            if (dto.getLeaveAlw() != null) {
                getMinSalary(dto);
                getSubsidy(dto);
                BigDecimal salary = getSalary(dto);
                dto.setPositionAlw(salary);
                BigDecimal deduction = getDeduction(dto);
                BigDecimal salaryAct = dto.getLeaveAlw().subtract(deduction).setScale(0, RoundingMode.HALF_UP);
                dto.setSalaryAct(salaryAct);
            }
            zeroToNull(dto);
        }
        String fileName ="工资银行表"+"_" +DateUtils.dateTimeNow() + ".xls";
        Map<String, List<PcSalaryNewDto>> listMap = newList.stream().collect(Collectors.groupingBy(PcSalaryNewDto::getDeptName)).entrySet().stream()
                .sorted((t1, t2) -> t1.getKey().compareToIgnoreCase(t2.getKey()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue,
                        LinkedHashMap::new
                ));
        Map<Integer, List<Map<String, Object>>> mapList = new HashMap<>();
        List<Map<String, Object>> exportList = new ArrayList<>();
        Map<String, Object> sheetData = new HashMap<>();
        sheetData.put("sheetName","汇总");
        sheetData.put("newList",newList);
        sheetData.put("date", LocalDate.now());
        sheetData.put("count", getSalaryActCount(newList));
        exportList.add(sheetData);
        mapList.put(0,exportList);
        int sheetIndex = 1;
        for (Map.Entry<String, List<PcSalaryNewDto>> entry : listMap.entrySet()) {
            String deptName = entry.getKey();
            List<PcSalaryNewDto> salaryList = entry.getValue();
            List<Map<String, Object>> list = new ArrayList<>();
            for (int i = 0; i < salaryList.size(); i++) {
                int j = i + 1;
                PcSalaryNewDto dto = salaryList.get(i);
                dto.setCfmFlag(String.valueOf(j));
            }
            Map<String, Object> data = new HashMap<>();
            data.put("sheetName", deptName);
            data.put("deptName", deptName);
            data.put("date", LocalDate.now());
            data.put("newList", salaryList);
            list.add(data);
            data.put("count", getSalaryActCount(salaryList));
            mapList.put(sheetIndex, list);
            sheetIndex++;
        }
        try {
            TemplateExportParams templateParams = new TemplateExportParams("templates/工资银行表模板.xls", true);
            ExcelUtils.setResponseZF(response, fileName);
            Workbook workbook = ExcelExportUtil.exportExcelClone(mapList,templateParams);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sheet = workbook.getSheetAt(i);
                sheet.createFreezePane(0, 3);
                sheet.setRepeatingRows(new CellRangeAddress(0, 2, 0, 10));
            }
            workbook.write(response.getOutputStream());
            workbook.close();
        } catch (Exception e) {
            log.error("工资银行表导出失败, 错误信息: {}", e.getMessage());
            throw new ServiceErrorException("工资银行表导出失败!");
        }
    }

    /**
     * 求汇总工资
     * @param list
     * @return
     */
    public BigDecimal getSalaryActCount(List<PcSalaryNewDto> list) {
        return list.stream().map(PcSalaryNewDto::getSalaryAct).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 导出工资条
     * @param request
     * @param response
     */
    @Override
    public void ExportPayslip(PcSalaryNewListRequest request, HttpServletResponse response) {
        request.setCompanyId(UserIdThread.get().getCompanyId());
        //查询职工
        request.setPosition("管理");
        List<PcSalaryNewDto> newList = personnelMapper.getPcSalaryNewList(request);
        if (ZStringUtils.isEmpty(newList)) {
            throw new ServiceErrorException("没有查询到工资签核明细信息!");
        }
        String fileName = "工资条" + "_" + DateUtils.dateTimeNow() + ".xlsx";
        LocalDate now = LocalDate.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年M月d日", Locale.CHINA);
        String formattedDate = now.format(formatter);
        try {
            Workbook workbook = new SXSSFWorkbook(-1);
            Sheet sheet = workbook.createSheet("薪资结算单");
            CellStyle cellStyle = createCustomCellStyle(workbook, "微软雅黑", (short) 16);
            CellStyle cellStyle2 = createCustomCellStyle(workbook, "宋体", (short) 12);
            CellStyle cellStyle3 = createCustomCellStyle(workbook, "宋体", (short) 12);
            CellStyle style = setBorderStyle(cellStyle2, BorderStyle.THIN, IndexedColors.BLACK.getIndex());
            CellStyle style2 = setBorderStyle(cellStyle3, BorderStyle.THIN, IndexedColors.BLACK.getIndex());
            style2.setAlignment(HorizontalAlignment.LEFT);
            //创建换行
            CreationHelper helper = workbook.getCreationHelper();
            //隐藏弟三列
            sheet.setColumnWidth(2, 0);
            int rowIndex = 0;
            for (int i = 0; i < newList.size(); i++) {
                PcSalaryNewDto dto = newList.get(i);
                Row row = sheet.createRow(rowIndex);
                row.setHeightInPoints(30);
                Cell cell = row.createCell(0);
                cell.setCellValue("薪资结算单");
                cell.setCellStyle(cellStyle);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 0, 12));

                Row row1 = sheet.getRow(rowIndex + 1);
                if (row1 == null) {
                    row1 = sheet.createRow(rowIndex + 1);
                }
                row1.setHeightInPoints(40);
                Cell cell1 = row1.createCell(0);
                cell1.setCellValue("部门");
                cell1.setCellStyle(style);
                Cell cell2 = row1.createCell(1);
                cell2.setCellValue(dto.getDeptName());
                cell2.setCellStyle(cellStyle2);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex + 1, rowIndex + 1, 1, 2));
                setRegionBorder(BorderStyle.THIN, new CellRangeAddress(rowIndex + 1, rowIndex + 1, 1, 2), sheet);

                Cell cell3 = row1.createCell(3);
                cell3.setCellValue("姓名");
                cell3.setCellStyle(style);

                Cell cell4 = row1.createCell(4);
                cell4.setCellValue(dto.getName());
                cell4.setCellStyle(style);
                cell4.setCellStyle(cellStyle2);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex + 1, rowIndex + 1, 4, 5));
                setRegionBorder(BorderStyle.THIN, new CellRangeAddress(rowIndex + 1, rowIndex + 1, 4, 5), sheet);

                Cell cell6 = row1.createCell(6);
                cell6.setCellValue("工号");
                cell6.setCellStyle(style);

                Cell cell7 = row1.createCell(7);
                cell7.setCellValue(dto.getWorkerId());
                cell7.setCellStyle(style);
                sheet.setColumnWidth(7, 2550);

                Cell cell8 = row1.createCell(8);
                cell8.setCellValue("岗位");
                cell8.setCellStyle(style);

                Cell cell9 = row1.createCell(9);
                cell9.setCellValue(dto.getWorkType());
                cell9.setCellStyle(style);

                Cell cell10 = row1.createCell(10);
                cell10.setCellValue(helper.createRichTextString("制单\n日期"));
                style.setWrapText(true);
                cell10.setCellStyle(style);

                Cell cell11 = row1.createCell(11);
                cell11.setCellValue(formattedDate);
                cell11.setCellStyle(style);
                cell11.setCellStyle(cellStyle2);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex + 1, rowIndex + 1, 11, 12));
                setRegionBorder(BorderStyle.THIN, new CellRangeAddress(rowIndex + 1, rowIndex + 1, 11, 12), sheet);

                Row row2 = sheet.getRow(rowIndex + 2);
                if (row2 == null) {
                    row2 = sheet.createRow(rowIndex + 2);
                }
                row2.setHeightInPoints(50);
                Cell rowCell0 = row2.createCell(0);
                rowCell0.setCellValue("月份");
                rowCell0.setCellStyle(style);

                Cell rowCell1 = row2.createCell(1);
                rowCell1.setCellValue("天数");
                rowCell1.setCellStyle(style);

                Cell rowCell2 = row2.createCell(2);
                rowCell2.setCellValue(helper.createRichTextString("应发\n工资2"));
                style.setWrapText(true);
                rowCell2.setCellStyle(style);

                Cell rowCell3 = row2.createCell(3);
                rowCell3.setCellValue(helper.createRichTextString("基本\n工资"));
                style.setWrapText(true);
                rowCell3.setCellStyle(style);

                Cell rowCell4 = row2.createCell(4);
                rowCell4.setCellValue(helper.createRichTextString("绩效\n福利"));
                style.setWrapText(true);
                rowCell4.setCellStyle(style);

                Cell rowCell5 = row2.createCell(5);
                rowCell5.setCellValue(helper.createRichTextString("加班\n津贴"));
                style.setWrapText(true);
                rowCell5.setCellStyle(style);

                Cell rowCell6 = row2.createCell(6);
                rowCell6.setCellValue(helper.createRichTextString("其他\n补贴"));
                style.setWrapText(true);
                rowCell6.setCellStyle(style);

                Cell rowCell7 = row2.createCell(7);
                rowCell7.setCellValue(helper.createRichTextString("应发\n工资"));
                style.setWrapText(true);
                rowCell7.setCellStyle(style);

                Cell rowCell8 = row2.createCell(8);
                rowCell8.setCellValue("预支");
                rowCell8.setCellStyle(style);

                Cell rowCell9 = row2.createCell(9);
                rowCell9.setCellValue(helper.createRichTextString("水电费及\n网络费"));
                style.setWrapText(true);
                rowCell9.setCellStyle(style);

                Cell rowCell10 = row2.createCell(10);
                rowCell10.setCellValue(helper.createRichTextString("代扣\n代缴"));
                style.setWrapText(true);
                rowCell10.setCellStyle(style);

                Cell rowCell11 = row2.createCell(11);
                rowCell11.setCellValue("其他");
                rowCell11.setCellStyle(style);

                Cell rowCell12 = row2.createCell(12);
                rowCell12.setCellValue(helper.createRichTextString("实发\n工资"));
                style.setWrapText(true);
                rowCell12.setCellStyle(style);

                Row row3 = sheet.getRow(rowIndex + 3);
                if (row3 == null) {
                    row3 = sheet.createRow(rowIndex + 3);
                }
                row3.setHeightInPoints(25);
                Cell row3Cell0 = row3.createCell(0);
                row3Cell0.setCellValue(dto.getYyyymm());
                row3Cell0.setCellStyle(style);

                Cell row3Cell1 = row3.createCell(1);
                BigDecimal attendanceAct = dto.getAttendanceAct() != null ? dto.getAttendanceAct(): BigDecimal.ZERO;
                row3Cell1.setCellValue(attendanceAct.doubleValue());
                row3Cell1.setCellStyle(style);

                Cell row3Cell2 = row3.createCell(2);
                BigDecimal leaveAlw = dto.getLeaveAlw() != null ? dto.getLeaveAlw().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                row3Cell2.setCellValue(leaveAlw.doubleValue());
                row3Cell2.setCellStyle(style);

                getMinSalary(dto);
                getBasicSalary(dto);
                getEmpSalaryCount(dto);
                Cell row3Cell3 = row3.createCell(3);
                row3Cell3.setCellValue(dto.getBasicSalary().doubleValue());
                row3Cell3.setCellStyle(style);

                Cell row3Cell4 = row3.createCell(4);
                row3Cell4.setCellValue(dto.getSalaryAlw().doubleValue());
                row3Cell4.setCellStyle(style);

                Cell row3Cell5 = row3.createCell(5);
                row3Cell5.setCellValue(dto.getOverSalary().doubleValue());
                row3Cell5.setCellStyle(style);

                Cell row3Cell6 = row3.createCell(6);
                BigDecimal minSalary = dto.getMinSalary() != null ? dto.getMinSalary().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                row3Cell6.setCellValue(minSalary.doubleValue());
                row3Cell6.setCellStyle(style);

                Cell row3Cell7 = row3.createCell(7);
                row3Cell7.setCellValue(dto.getPositionAlw().doubleValue());
                row3Cell7.setCellStyle(style);

                Cell row3Cell8 = row3.createCell(8);
                BigDecimal repayM = dto.getRepayM() != null ? dto.getRepayM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                if (repayM.compareTo(BigDecimal.ZERO) != 0) {
                    row3Cell8.setCellValue(repayM.doubleValue());
                }
                row3Cell8.setCellStyle(style);

                Cell row3Cell9 = row3.createCell(9);
                BigDecimal waterEleM = dto.getWaterEleM() != null ? dto.getWaterEleM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                if (waterEleM.compareTo(BigDecimal.ZERO) != 0) {
                    row3Cell9.setCellValue(waterEleM.doubleValue());
                }
                row3Cell9.setCellStyle(style);

                Cell row3Cell10 = row3.createCell(10);
                BigDecimal unionM = dto.getUnionM() != null ? dto.getUnionM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal taxM = dto.getTaxM() != null ? dto.getTaxM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                if (unionM.add(taxM).compareTo(BigDecimal.ZERO) != 0){
                    row3Cell10.setCellValue(unionM.add(taxM).doubleValue());
                }
                row3Cell10.setCellStyle(style);

                Cell row3Cell11 = row3.createCell(11);
                BigDecimal otherM = dto.getOtherM() != null ? dto.getOtherM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal clothesM = dto.getClothesM() != null ? dto.getClothesM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal mealM = dto.getMealM() != null ? dto.getMealM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal waterM = dto.getWaterM() != null ? dto.getWaterM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal absenceM = dto.getAbsenceM() != null ? dto.getAbsenceM().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal carFee = dto.getCarFee() != null ? dto.getCarFee().setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                otherM = otherM.add(clothesM).add(mealM).add(waterM).add(absenceM).add(carFee);
                if (otherM.compareTo(BigDecimal.ZERO) != 0) {
                    row3Cell11.setCellValue(otherM.doubleValue());
                }
                row3Cell11.setCellStyle(style);

                Cell row3Cell12 = row3.createCell(12);
                BigDecimal salaryAct = dto.getPositionAlw().subtract(waterEleM).subtract(unionM).subtract(taxM).subtract(otherM).subtract(repayM);
                row3Cell12.setCellValue(salaryAct.doubleValue());
                row3Cell12.setCellStyle(style);

                Row row4 = sheet.getRow(rowIndex + 4);
                if (row4 == null) {
                    row4 = sheet.createRow(rowIndex + 4);
                }
                row4.setHeightInPoints(65);
                Cell row4Cell0 = row4.createCell(0);
                row4Cell0.setCellValue("备注:");
                row4Cell0.setCellStyle(style);

                Cell row4Cell1 = row4.createCell(1);
                row4Cell1.setCellValue("1、本工资单所列“其他补贴”指用人单位应承担的员工社会保险费用。\n2、本工资单签收之日,即视为用人单位已将当月以及之前的所有工资及费用（含医社保、加班工资等）结算完毕并支付给员工。");
                style2.setWrapText(true);
                row4Cell1.setCellStyle(style2);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex+4, rowIndex+4, 1, 12));
                setRegionBorder(BorderStyle.THIN, new CellRangeAddress(rowIndex + 4, rowIndex + 4, 1, 12), sheet);

                Row row5 = sheet.getRow(rowIndex + 5);
                if (row5 == null) {
                    row5 = sheet.createRow(rowIndex + 5);
                }
                row5.setHeightInPoints(25);

                Cell row4Cell8 = row5.createCell(8);
                row4Cell8.setCellValue("签字:");
                row4Cell8.setCellStyle(style2);
                sheet.addMergedRegion(new CellRangeAddress(rowIndex+5, rowIndex+5, 8, 9));
                setRegionBorder(BorderStyle.NONE, new CellRangeAddress(rowIndex + 5, rowIndex + 5, 8, 9), sheet);

                rowIndex += 19;
            }
            // 获取打印设置
            PrintSetup printSetup = sheet.getPrintSetup();
            // 设置纸张大小为 A5
            printSetup.setPaperSize(PrintSetup.A5_PAPERSIZE);
            // 设置纸张方向为横向
            printSetup.setLandscape(true);
            //禁用自适应，重新设置页边距
            sheet.setAutobreaks(false);
            // 设置打印缩放比例为 85%
            printSetup.setScale((short) 85);
            // 禁用适配宽高比例
            printSetup.setFitWidth((short) 1);
            printSetup.setFitHeight((short) 0);
            // 设置页边距 (单位为英寸，1英寸=2.54厘米)
            sheet.setMargin(Sheet.TopMargin, 1.5 / 2.54);  // 顶部边距为1.5厘米
            sheet.setMargin(Sheet.BottomMargin, 0.0 / 2.54); // 底部边距为0
            sheet.setMargin(Sheet.LeftMargin, 1.5 / 2.54);  // 左边距为1.5厘米
            sheet.setMargin(Sheet.RightMargin, 0.0 / 2.54); // 右边距为0
            sheet.setMargin(Sheet.HeaderMargin, 0.8 / 2.54); // 页眉边距为0.8厘米
            sheet.setMargin(Sheet.FooterMargin, 0.8 / 2.54); // 页脚边距为0.8厘米
            ExcelUtils.setResponseZF(response, fileName);
            ExcelPoiUtils.writeExcelToResponse(workbook, response);
            workbook.close();
        } catch (Exception e) {
            log.error("薪资结算单导出失败, 错误信息: {}", e.getMessage());
            throw new ServiceErrorException("薪资结算单导出失败!");
        }
    }

    /**
     * 设置单个单元格样式
     * @param style
     * @param border
     * @param index
     */
    private CellStyle setBorderStyle(CellStyle style,BorderStyle border,short index) {
        style.setBorderTop(border); // 上边框
        style.setBorderBottom(border); // 下边框
        style.setBorderLeft(border); // 左边框
        style.setBorderRight(border); // 右边框
        // 设置边框颜色为黑色
        style.setBottomBorderColor(index);
        style.setTopBorderColor(index);
        style.setLeftBorderColor(index);
        style.setRightBorderColor(index);
        return style;
    }

    /**
     * 设置合并单元格边框
     * @param border
     * @param region
     * @param sheet
     */
    private void setRegionBorder(BorderStyle border, CellRangeAddress region, Sheet sheet) {
        RegionUtil.setBorderBottom(border, region,sheet);
        RegionUtil.setBorderTop(border,region , sheet);
        RegionUtil.setBorderLeft(border,region, sheet);
        RegionUtil.setBorderRight(border, region, sheet);
    }

    /**
     * 创建单元格样式
     * @param workbook
     * @param fontName
     * @param fontSize
     * @return
     */
    public static CellStyle createCustomCellStyle(Workbook workbook, String fontName, short fontSize) {
        // 创建字体
        Font font = workbook.createFont();
        font.setFontName(fontName);
        font.setFontHeightInPoints(fontSize);
        // 创建单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return cellStyle;
    }

    /**
     * 计算基本工资
     * @param dto
     */
    public void getBasicSalary(PcSalaryNewDto dto) {
        if (!ObjectUtils.isEmpty(dto.getWorkerId())){
            BigDecimal leaveAlw = dto.getLeaveAlw()!=null?dto.getLeaveAlw():BigDecimal.ZERO;
            BigDecimal minSalary = dto.getMinSalary()!=null?dto.getMinSalary():BigDecimal.ZERO;
            BigDecimal baseSalary=BigDecimal.ZERO;
            if (leaveAlw.compareTo(BigDecimal.valueOf(2500)) <= 0) {
                baseSalary=leaveAlw.subtract(minSalary).setScale(0, RoundingMode.HALF_UP);
            } else if (leaveAlw.compareTo(BigDecimal.valueOf(4000)) <= 0) {
                baseSalary=BigDecimal.valueOf(2000);
            } else if (leaveAlw.compareTo(BigDecimal.valueOf(6000)) <= 0){
                baseSalary=BigDecimal.valueOf(2500);
            }else if (leaveAlw.compareTo(BigDecimal.valueOf(8000)) <= 0){
                baseSalary=BigDecimal.valueOf(3000);
            }else {
                baseSalary=BigDecimal.valueOf(4000);
            }
            dto.setBasicSalary(baseSalary);
        }
    }

    /**
     * 计算管理员工工资
     * @param dto
     */
    public void getEmpSalaryCount(PcSalaryNewDto dto) {
        if (!ObjectUtils.isEmpty(dto.getWorkerId())){
            BigDecimal leaveAlw = dto.getLeaveAlw()!=null?dto.getLeaveAlw():BigDecimal.ZERO;
            BigDecimal minSalary = dto.getMinSalary()!=null?dto.getMinSalary():BigDecimal.ZERO;
            BigDecimal baseSalary = dto.getBasicSalary()!=null?dto.getBasicSalary():BigDecimal.ZERO;
            BigDecimal salaryAlw = getSalaryCount(0.45, leaveAlw, baseSalary, minSalary);
            BigDecimal overSalary = getSalaryCount(0.55, leaveAlw, baseSalary, minSalary);
            dto.setSalaryAlw(salaryAlw);
            dto.setOverSalary(overSalary);
            //应发工资
            BigDecimal positionAlw  = baseSalary.add(salaryAlw).add(overSalary).add(minSalary);
            dto.setPositionAlw(positionAlw);
        }
    }

    /**
     * 管理计算绩效公式
     * @param value 百分比
     * @param leaveAlw 统计
     * @param baseSalary 基本工资
     * @param minSalary 补贴
     * @return
     */
    public BigDecimal getSalaryCount(Double value,BigDecimal leaveAlw,BigDecimal baseSalary,BigDecimal minSalary){
        return (leaveAlw.subtract(baseSalary).subtract(minSalary)).multiply(BigDecimal.valueOf(value)).setScale(0, RoundingMode.HALF_UP);
    }

    /**
     * 返回月份
     * @return
     */
    @Override
    public ResponseInfo getMonth() {
        return ResponseInfo.ok(personnelMapper.getMonth(), "操作成功!");
    }
}